package ch.unisi.inf.java.project.riskbami.model;

import java.util.Random;
import java.util.ArrayList;
import java.awt.event.MouseListener;
/**
 * The class Game is the main class of our application.
 * The application RiskBaMi is a Java implementation of the well
 * known game Risk. Originally it is a board game but in this version
 * the users will play using computers.
 * 
 * @author minellir@lu.unisi.ch
 */
public final class Game {
    private final ArrayList<Player> players;
    private int current;
    private int phase;
    private ArrayList<Country> countries;
    private ArrayList<Card> deck;
    private final Random r = new Random();
    private final ArrayList<GameListener> listeners;
    private final ArrayList<MouseListener> mouseListeners;
    private boolean finished;
    
    /**
     * The constructor of the class Game.
     * 
     * @param players The players, Player[], involved in the war
     */
    public Game(final ArrayList<Player> players){
        this.countries = new ArrayList<Country>();
        this.deck = new ArrayList<Card>();
        this.players = players;
        this.listeners = new ArrayList<GameListener>();
        this.mouseListeners = new ArrayList<MouseListener>();
        this.finished = false;
        current = 0;
        phase = 0;
        for(Player p : players){
            p.setGame(this);
        }
    }
    
    public final void setIsFinished(){
        this.finished = true;
    }
    
    public final boolean getIsFinished(){
        return finished;
    }
    
    public final ArrayList<Player> getPlayers(){
        return players;
    }
    
    /**
     * getOwnerOf gives us back the owner of a given country.
     * 
     * @param country The country involved in the request
     * @return Player The player that own the given country. Null if not found.
     */
    public final Player getOwnerOf(final Country country){
        Player owner = null;
        for(Player p : players){
            if(p.getOwnedCountries().contains(country)){
                owner = p;
            }
        }
        return owner;
    }
    
    /**
     * randomCountryDistributor runs the algorithm that distributes the territories to the players.
     * The distribution starts from a random player and distribute the available countries to all the player.
     * Some players may have more countries than others.
     */
    public final void randomCountryDistributor(){
        final ArrayList<Country> assignCountries = new ArrayList<Country>();
        assignCountries.addAll(countries);
        final Random randomGenerator = new Random();          
        final int numberOfPlayers = players.size(); 
        int i = randomGenerator.nextInt(numberOfPlayers);
        int numberOfCountries = assignCountries.size();
        while (numberOfCountries>0){
            final int randomInt = randomGenerator.nextInt(numberOfCountries--);
            players.get(i++%numberOfPlayers).addOwnedCountry(assignCountries.get(randomInt));
            assignCountries.remove(randomInt);
            }       
    }   
    
    /**
     * createDeck generates a new deck for the current map.
     * Each game a new deck is created. Are generated as many cards as the number of
     * countries involved in the game (map) and 2 wild cards.
     */
    public final void createDeck(){
        final CardType[] types = {CardType.INFANTRY, CardType.CAVALRY, CardType.ARTILLERY};
        this.deck.add(new Card(countries.size(), CardType.JOLLY, null));
        this.deck.add(new Card(countries.size()+1, CardType.JOLLY, null));
        int id = 0;
        for(final Country c : countries){
            this.deck.add(new Card(id++, types[r.nextInt(types.length - 1)], c));
        }
    }
    
    /**
     * shuffleDeck provide to arrange a set of card in a random order. The method automatically
     * get the deck of this game arrange the deck and set the new shuffled deck as the actual
     * deck of the game.
     */
    public final void shuffleDeck(){
        final ArrayList<Card> originalDeck = this.deck;
        final ArrayList<Card> shuffledDeck = new ArrayList<Card>();
        final Random rand = new Random();
        int remainingCards = originalDeck.size();
        while(remainingCards>0){
            final Card randomCard = originalDeck.get(rand.nextInt(remainingCards));
            shuffledDeck.add(randomCard);
            originalDeck.remove(randomCard);
            remainingCards--;
        }
        this.deck = shuffledDeck;
    }
    
    public final void readdToDeck(Card c){
        this.deck.add(c);
    }
    
    /**
     * drawCard return the first card of the deck, the one that is on top of the deck
     * to draw.
     * 
     * @return Card The card that has to be draw
     */
    public final Card drawCard(){
        final Card drawed = this.deck.get(0);
        this.deck.remove(drawed);
        return drawed;
    }
    
    /**
     * addAllCountries takes a list of countries and set these countries
     * as the actual for the current (this) game.
     * 
     * @param countries The list of countries to add.
     */
    public final void addAllCountries(final ArrayList<Country> countries){
        this.countries = countries;
    }
    
    /**
     * getCountries returns the countries of the actual game
     * 
     * @return ArrayList<Country> The countries that makes the map of the game
     */
    public final ArrayList<Country> getCountries(){
        return countries;
    }
    
    /**
     * getCurrentPlayer returns the player that is playing now
     * 
     * @return Player The current player
     */
    public final Player getCurrentPlayer(){
        final int numberOfPlayers = players.size();
        if(numberOfPlayers > 1){
            return players.get(current);
        } else { 
            return players.get(0); 
        }
    }
    
    public final void removePlayer(final Player p){
        players.remove(p);
    }
    
    /**
     * switchNextPlayer provide to change the variable int current that holds the
     * index of the position in the Player[], players, accordingly to its size.
     */
    public final void switchNextPlayer(){
        final int numberOfPlayers = players.size();
        current = (current+1)%numberOfPlayers;
        setPhaseZero();
        firePlayerChanged();
    }
    
    
    /**
     * getPhase return the phase of the game
     * 0 - GetArmies
     * 1 - PlaceArmies
     * 2 - Attack
     * 3 - Fortify
     * 
     * @return int The actual phase of the game
     */
    public final int getTurnPhase(){
        return phase;
    }
   
    /**
     * setPhaseZero is a setter (better a resetter) for the phase field 
     */
    public final void setPhaseZero(){
        phase = 0;
        firePhaseChanged();
        
    }
    
    /**
     * switchNextPhase provide to change the variable int phase that holds the
     * phase identifier (between 0 and 3)
     */
    public final void switchNextPhase(){
        phase = (phase+1) % 4;
        firePhaseChanged();
    }
    
    /**
     * getNumberOfPlayers give us the number of players
     * 
     * @return int The number of players
     */
    public final int getNumberOfPlayers(){
        return players.size();
    }
    
    /**
     * The following methods add and remove a GameListener
     */
    public void addGameListener(final GameListener li){
        listeners.add(li);
    }
    
    public void removeGameListener(final GameListener li){
        listeners.remove(li);
    }
    
    /**
     * The following methods notice the GameListeners that something changed.
     */
    public void firePhaseChanged(){
        for(final GameListener li : listeners){
            li.phaseChanged();
        }
    }
    
     public void firePlayerChanged(){
         for(final GameListener li : listeners){
             li.playerChanged();
         }
     }
}
